package com.greateamd.timetableasy.server.dao.impl;

import java.sql.SQLException;

import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.greateamd.timetableasy.client.domain.AuthType;
import com.greateamd.timetableasy.domain.Auth;
import com.greateamd.timetableasy.domain.Campus;
import com.greateamd.timetableasy.domain.Course;
import com.greateamd.timetableasy.domain.Event;
import com.greateamd.timetableasy.domain.Promotion;
import com.greateamd.timetableasy.domain.StudyPeriod;
import com.greateamd.timetableasy.domain.University;
import com.greateamd.timetableasy.domain.User;
import com.greateamd.timetableasy.server.dao.EventDAO;
import com.greateamd.timetableasy.server.dao.util.AbstractHibernateDAO;



@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED, readOnly = false)	
@Repository("eventDAO")
public class EventDAOImpl extends AbstractHibernateDAO<Event,Integer> implements EventDAO {

	public EventDAOImpl() {
		super(new Event());
	}
	
	@Override
	public void update(final Event event) throws HibernateException, SQLException, IllegalAccessException {
		//
		final User loggedUser = getHibernateTemplate().execute(new HibernateCallback<User>() {
			@Override
			public User doInHibernate(Session session) throws HibernateException, SQLException {
				final String username = SecurityContextHolder.getContext().getAuthentication().getName();
				return (User) session.get(User.class, username);
			}});
		
		//
		if(loggedUser == null)
			throw new IllegalAccessException("User logged cannot be found");
		
		boolean canAccess = false;
		//Check right
		switch(event.getEventType()){
		case USER:
			//If it's the events of a user, he have right to edit them
			if(loggedUser.getEvents().contains(event)){
				loggedUser.getEvents().remove(event);
				loggedUser.getEvents().add(event);
				canAccess = true;
			}
			break;
		case UNIVERSITY:
			if(loggedUser.getAuths().contains(new Auth(loggedUser,AuthType.ROLE_ADMIN))){
				final University university = getHibernateTemplate().execute(new HibernateCallback<University>() {
					@Override
					public University doInHibernate(Session session) throws HibernateException, SQLException {
						String hql = "SELECT distinct(university) " +
						 "FROM University university " +
						 "JOIN university.events event " +
						 	"WITH event.id = :eventId ";
						Query query = session.createQuery(hql);
						query.setParameter("eventId", event.getId());
						University university = (University) query.uniqueResult();
						return university;
					}});
				if(university != null && university.getEvents().contains(event)){
					university.getEvents().remove(event);
					university.getEvents().add(event);
				}
				canAccess = true;
			}
			break;
		case STUDY_PERIOD:
			if(loggedUser.getAuths().contains(new Auth(loggedUser,AuthType.ROLE_ADMIN))
			|| loggedUser.getAuths().contains(new Auth(loggedUser,AuthType.ROLE_CAMPUS_MANAGER))){
				final StudyPeriod sp = getHibernateTemplate().execute(new HibernateCallback<StudyPeriod>() {
					@Override
					public StudyPeriod doInHibernate(Session session) throws HibernateException, SQLException {
						String hql = "SELECT distinct(sp) " +
						 "FROM StudyPeriod sp " +
						 "JOIN sp.events event " +
						 	"WITH event.id = :eventId ";
						Query query = session.createQuery(hql);
						query.setParameter("eventId", event.getId());
						StudyPeriod university = (StudyPeriod) query.uniqueResult();
						return university;
					}});
				if(sp != null && sp.getEvents().contains(event)){
					sp.getEvents().remove(event);
					sp.getEvents().add(event);
				}
				canAccess = true;
			}
			break;
		case CAMPUS:
			if(loggedUser.getAuths().contains(new Auth(loggedUser,AuthType.ROLE_CAMPUS_MANAGER))){
				canAccess = getHibernateTemplate().execute(new HibernateCallback<Boolean>() {
					@Override
					public Boolean doInHibernate(Session session) throws HibernateException, SQLException {
						String hql = "SELECT campus " +
						 "FROM Campus campus " +
						 "JOIN campus.events event " +
						 	"WITH event.id = :eventId";
						Query query = session.createQuery(hql);
						query.setParameter("eventId", event.getId());
						Campus campusTarget = (Campus) query.uniqueResult();
						
						if(campusTarget != null && campusTarget.getManagers().contains(loggedUser) && campusTarget.getEvents().contains(event)){
							campusTarget.getEvents().remove(event);
							campusTarget.getEvents().add(event);
							return true;
						}
						else
							return false;
					}});
			}
			break;
		case PROMOTION:
			if(loggedUser.getAuths().contains(new Auth(loggedUser,AuthType.ROLE_CAMPUS_MANAGER))){
				canAccess = getHibernateTemplate().execute(new HibernateCallback<Boolean>() {
					@Override
					public Boolean doInHibernate(Session session) throws HibernateException, SQLException {
						String hql = "SELECT promotion " +
						 "FROM Promotion promotion " +
						 "JOIN promotion.events event " +
						 	"WITH event.id = :eventId";
						Query query = session.createQuery(hql);
						query.setParameter("eventId", event.getId());
						Promotion promotionTarget = (Promotion) query.uniqueResult();


						if(promotionTarget != null && promotionTarget.getCampus().getManagers().contains(loggedUser) && promotionTarget.getEvents().contains(event)){
							promotionTarget.getEvents().remove(event);
							promotionTarget.getEvents().add(event);
							return true;
						}
						else
							return false;
					}});
			}
			break;
			case COURSE:
				if(loggedUser.getAuths().contains(new Auth(loggedUser,AuthType.ROLE_CAMPUS_MANAGER))){
					canAccess = getHibernateTemplate().execute(new HibernateCallback<Boolean>() {
						@Override
						public Boolean doInHibernate(Session session) throws HibernateException, SQLException {
							String hql = "SELECT course " +
							 "FROM Course course " +
							 "JOIN course.events event " +
							 	"WITH event.id = :eventId";
							Query query = session.createQuery(hql);
							query.setParameter("eventId", event.getId());
							Course courseTarget = (Course) query.uniqueResult();

							if(courseTarget.getPromotion() == null && !loggedUser.getAuths().contains(new Auth(loggedUser, AuthType.ROLE_ADMIN))){
								return false;
							} else if(courseTarget != null && courseTarget.getPromotion().getCampus().getManagers().contains(loggedUser) && courseTarget.getEvents().contains(event)){
								courseTarget.getEvents().remove(event);
								courseTarget.getEvents().add(event);
								return true;
							} else
								return false;
						}});
				}
				break;
		}
		if(canAccess){
			getHibernateTemplate().execute(new HibernateCallback<Void>() {
		         public Void doInHibernate(Session session) throws HibernateException,SQLException {
		        	 session.evict(session.get(Event.class, event.getId()));
		        	 session.update(event);
		        	 return null;
		         }});
		} else {
			throw new IllegalAccessException("User enought right to perform this operation");
		}
	}
	
	@Override
	public void delete(final Event event) throws HibernateException, SQLException, IllegalAccessException {
		//
		final User loggedUser = getHibernateTemplate().execute(new HibernateCallback<User>() {
			@Override
			public User doInHibernate(Session session) throws HibernateException, SQLException {
				final String username = SecurityContextHolder.getContext().getAuthentication().getName();
				return (User) session.get(User.class, username);
			}});
		
		//
		if(loggedUser == null)
			throw new IllegalAccessException("User logged cannot be found");
		
		boolean canAccess = false;
		//Check right
		switch(event.getEventType()){
		case USER:
			//If it's the events of a user, he have right to edit them
			if(loggedUser.getEvents().contains(event)){
				loggedUser.getEvents().remove(event);
				canAccess = true;
			}
			break;
		case UNIVERSITY:
			if(loggedUser.getAuths().contains(new Auth(loggedUser,AuthType.ROLE_ADMIN))){
				final University university = getHibernateTemplate().execute(new HibernateCallback<University>() {
					@Override
					public University doInHibernate(Session session) throws HibernateException, SQLException {
						String hql = "SELECT distinct(university) " +
						 "FROM University university " +
						 "JOIN university.events event " +
						 	"WITH event.id = :eventId ";
						Query query = session.createQuery(hql);
						query.setParameter("eventId", event.getId());
						University university = (University) query.uniqueResult();
						return university;
					}});
				if(university != null && university.getEvents().contains(event)){
					university.getEvents().remove(event);
				}
				canAccess = true;
			}
			break;
		case STUDY_PERIOD:
			if(loggedUser.getAuths().contains(new Auth(loggedUser,AuthType.ROLE_ADMIN))
			|| loggedUser.getAuths().contains(new Auth(loggedUser,AuthType.ROLE_CAMPUS_MANAGER))){
				final StudyPeriod sp = getHibernateTemplate().execute(new HibernateCallback<StudyPeriod>() {
					@Override
					public StudyPeriod doInHibernate(Session session) throws HibernateException, SQLException {
						String hql = "SELECT distinct(sp) " +
						 "FROM StudyPeriod sp " +
						 "JOIN sp.events event " +
						 	"WITH event.id = :eventId ";
						Query query = session.createQuery(hql);
						query.setParameter("eventId", event.getId());
						StudyPeriod university = (StudyPeriod) query.uniqueResult();
						return university;
					}});
				if(sp != null && sp.getEvents().contains(event)){
					sp.getEvents().remove(event);
				}
				canAccess = true;
			}
			break;
		case CAMPUS:
			if(loggedUser.getAuths().contains(new Auth(loggedUser,AuthType.ROLE_CAMPUS_MANAGER))){
				canAccess = getHibernateTemplate().execute(new HibernateCallback<Boolean>() {
					@Override
					public Boolean doInHibernate(Session session) throws HibernateException, SQLException {
						String hql = "SELECT campus " +
						 "FROM Campus campus " +
						 "JOIN campus.events event " +
						 	"WITH event.id = :eventId";
						Query query = session.createQuery(hql);
						query.setParameter("eventId", event.getId());
						Campus campusTarget = (Campus) query.uniqueResult();
						
						if(campusTarget != null && campusTarget.getManagers().contains(loggedUser) && campusTarget.getEvents().contains(event)){
							campusTarget.getEvents().remove(event);
							return true;
						}
						else
							return false;
					}});
			}
			break;
		case PROMOTION:
			if(loggedUser.getAuths().contains(new Auth(loggedUser,AuthType.ROLE_CAMPUS_MANAGER))){
				canAccess = getHibernateTemplate().execute(new HibernateCallback<Boolean>() {
					@Override
					public Boolean doInHibernate(Session session) throws HibernateException, SQLException {
						String hql = "SELECT promotion " +
						 "FROM Promotion promotion " +
						 "JOIN promotion.events event " +
						 	"WITH event.id = :eventId";
						Query query = session.createQuery(hql);
						query.setParameter("eventId", event.getId());
						Promotion promotionTarget = (Promotion) query.uniqueResult();


						if(promotionTarget != null && promotionTarget.getCampus().getManagers().contains(loggedUser) && promotionTarget.getEvents().contains(event)){
							promotionTarget.getEvents().remove(event);
							return true;
						}
						else
							return false;
					}});
			}
			break;
			case COURSE:
				if(loggedUser.getAuths().contains(new Auth(loggedUser,AuthType.ROLE_CAMPUS_MANAGER))){
					canAccess = getHibernateTemplate().execute(new HibernateCallback<Boolean>() {
						@Override
						public Boolean doInHibernate(Session session) throws HibernateException, SQLException {
							String hql = "SELECT course " +
							 "FROM Course course " +
							 "JOIN course.events event " +
							 	"WITH event.id = :eventId";
							Query query = session.createQuery(hql);
							query.setParameter("eventId", event.getId());
							Course courseTarget = (Course) query.uniqueResult();

							if(courseTarget.getPromotion() == null && !loggedUser.getAuths().contains(new Auth(loggedUser, AuthType.ROLE_ADMIN))){
								return false;
							} else if(courseTarget != null && courseTarget.getPromotion().getCampus().getManagers().contains(loggedUser) && courseTarget.getEvents().contains(event)){
								courseTarget.getEvents().remove(event);
								return true;
							} else
								return false;
						}});
				}
				break;
		}
		if(canAccess){
			getHibernateTemplate().execute(new HibernateCallback<Void>() {
		         public Void doInHibernate(Session session) throws HibernateException,SQLException {
		        	 session.evict(session.get(Event.class, event.getId()));
		        	 session.delete(event);
		        	 return null;
		         }});
		} else {
			throw new IllegalAccessException("User enought right to perform this operation");
		}
	}
}